package main

import (
    "fmt"
    "runtime"
    "sync"
    "time"
)

// --- Goroutine ---
// Goroutine 是由 Go 运行时管理的轻量级线程。
// 创建一个 Goroutine 非常简单，只需在函数调用前加上 go 关键字。

func say(s string) {
    for i := 0; i < 5; i++ {
        // runtime.Gosched() 用于让出 CPU 时间片，让其他 goroutine 运行
        // 这有助于演示并发效果
        runtime.Gosched()
        fmt.Println(s)
    }
}

// --- Channels ---
// Channel 是 Goroutine 之间通信的管道，遵循 "不要通过共享内存来通信，而要通过通信来共享内存" 的哲学。
// 可以使用 make 创建一个 Channel。

func sum(s []int, c chan int) {
    total := 0
    for _, v := range s {
        total += v
    }
    // 将计算结果发送到 channel
    c <- total
}

// --- Buffered Channels ---
// 带缓冲的 Channel 可以在满载之前接收一定数量的值，而无需对应的接收方准备好。

func bufferedChannelExample() {
    // 创建一个缓冲区大小为 2 的 channel
    ch := make(chan int, 2)
    ch <- 1 // 发送不会阻塞，因为缓冲区未满
    ch <- 2
    // ch <- 3 // 这一行会阻塞，因为缓冲区已满

    fmt.Println(<-ch) // 接收
    fmt.Println(<-ch)
}

// --- Select ---
// select 语句让 Goroutine 可以等待多个通信操作。
// select 会阻塞，直到其中一个 case 可以运行，然后执行那个 case。
// 如果多个 case 都准备好，会随机选择一个执行。

func fibonacci(c, quit chan int) {
    x, y := 0, 1
    for {
        select {
        case c <- x:
            x, y = y, x+y
        case <-quit:
            fmt.Println("退出")
            return
        default:
            // 如果没有 case 准备好，执行 default
            // 这可以用来避免 select 死锁
            time.Sleep(50 * time.Millisecond)
            fmt.Println(".")
        }
    }
}

// --- sync.WaitGroup ---
// WaitGroup 用于等待一组 Goroutine 完成执行。
// 它内部有一个计数器，主 goroutine 可以调用 Add() 来增加计数，
// 每个 goroutine 在退出时调用 Done() 来减少计数。
// 主 goroutine 调用 Wait() 来阻塞，直到计数器变为零。

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done() // 在 goroutine 退出时调用 Done()
    fmt.Printf("Worker %d 正在工作\n", id)
    time.Sleep(time.Second)
    fmt.Printf("Worker %d 完成工作\n", id)
}

// --- Mutex (互斥锁) ---
// 虽然 Go 推荐用 Channel 通信，但在某些场景下，使用互斥锁来保护共享数据更直接。
// Mutex 用于保证在任何时刻只有一个 goroutine 能访问共享资源。

type SafeCounter struct {
    v   map[string]int
    mux sync.Mutex
}

func (c *SafeCounter) Inc(key string) {
    c.mux.Lock() // 在访问前加锁
    c.v[key]++
    c.mux.Unlock() // 访问后解锁
}

func (c *SafeCounter) Value(key string) int {
    c.mux.Lock()
    defer c.mux.Unlock() // 使用 defer 确保解锁
    return c.v[key]
}

func main() {
    // --- Goroutine ---
    fmt.Println("--- Goroutine ---")
    // 启动一个新的 goroutine 来执行 say 函数
    go say("world")
    say("hello")

    // 让 main goroutine 稍微等待一下，以便其他 goroutine 有时间执行
    time.Sleep(100 * time.Millisecond)
    fmt.Println("Goroutine 演示结束\n")

    // --- Channels ---
    fmt.Println("--- Channels ---")
    s := []int{7, 2, 8, -9, 4, 0}
    c := make(chan int) // 创建一个无缓冲的 channel
    go sum(s[:len(s)/2], c)
    go sum(s[len(s)/2:], c)

    // 从 channel 接收数据，这个操作会阻塞，直到有数据发送过来
    x, y := <-c, <-c
    fmt.Println("s 的和是:", x, y, x+y)
    fmt.Println()

    // --- Buffered Channels ---
    fmt.Println("--- Buffered Channels ---")
    bufferedChannelExample()
    fmt.Println()

    // --- Select ---
    fmt.Println("--- Select ---")
    c_quit := make(chan int)
    c_fib := make(chan int)
    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println("接收到:", <-c_fib)
        }
        c_quit <- 0
    }()
    fibonacci(c_fib, c_quit)
    fmt.Println()

    // --- sync.WaitGroup ---
    fmt.Println("--- sync.WaitGroup ---")
    var wg sync.WaitGroup
    for i := 1; i <= 3; i++ {
        wg.Add(1) // 为每个 worker 增加计数
        go worker(i, &wg)
    }
    wg.Wait() // 等待所有 worker 完成
    fmt.Println("所有 Worker 都已完成")
    fmt.Println()

    // --- Mutex ---
    fmt.Println("--- Mutex ---")
    counter := SafeCounter{v: make(map[string]int)}
    for i := 0; i < 1000; i++ {
        go counter.Inc("somekey")
    }
    time.Sleep(time.Second) // 等待所有 goroutine 完成
    fmt.Println("counter 的最终值:", counter.Value("somekey"))
}